package com.snail.billing.util;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class AesUtil {
	private static final String AES_ALGORITHM = "AES";
	private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

	static {
		System.loadLibrary("snail-billing");
	}

	public static native String key();

	public static String encrypt(String data) {
		return aesEncryptHex(data, key());
	}

	public static String decrypt(String data) {
		return aesDecryptHex(data, key());
	}

	public static String aesEncryptHex(String data, String key) {
		return encrypt(data, key, AES_ALGORITHM);
	}

	public static String aesDecryptHex(String data, String key) {
		return decrypt(data, key, AES_ALGORITHM);
	}

	public static String encrypt(String data, String key, String algorithm) {
		return hexEncode(cipher(data.getBytes(), key.getBytes(), algorithm, Cipher.ENCRYPT_MODE));
	}

	public static String decrypt(String data, String key, String algorithm) {
		return new String(cipher(hexDecode(data), key.getBytes(), algorithm, Cipher.DECRYPT_MODE));
	}

	private static byte[] cipher(byte[] data, byte[] key, String algorithm, int mode) {
		try {
			Cipher cipher = Cipher.getInstance(algorithm);
			cipher.init(mode, new SecretKeySpec(key, cipher.getAlgorithm()));
			return cipher.doFinal(data);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private static String hexEncode(byte[] input) {
		return new String(toHex(input, false));
	}

	private static byte[] hexDecode(String input) {
		return parseHex(input.toCharArray());
	}

	private static byte[] parseHex(char[] data) {
		int len = data.length;
		if ((len & 0x01) != 0) {
			throw new IllegalArgumentException("Odd number of characters.");
		}
		byte[] out = new byte[len >> 1];

		// two characters form the hex value.
		for (int i = 0, j = 0; j < len; i++) {
			int f = toDigit(data[j], j) << 4;
			j++;
			f = f | toDigit(data[j], j);
			j++;
			out[i] = (byte) (f & 0xFF);
		}

		return out;
	}

	private static int toDigit(char ch, int index) {
		int digit = Character.digit(ch, 16);
		if (digit == -1) {
			throw new IllegalArgumentException("Illegal hexadecimal character " + ch + " at index " + index);
		}
		return digit;
	}

	private static char[] toHex(byte[] data, boolean toLowerCase) {
		char[] toDigits = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER;
		int l = data.length;
		char[] out = new char[l << 1];
		// two characters form the hex value.
		for (int i = 0, j = 0; i < l; i++) {
			out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
			out[j++] = toDigits[0x0F & data[i]];
		}
		return out;
	}
}
